/**
 * Kernel Monkeys
 * Andrew Boguk
 * Sergiy Kulyk
 * Nicholas Swartzendruber
 */

/*
 * Scheduler ADT that handles context switches and semaphores.
 *
 * @Author: Andrew Boguk
 */

#include <stdio.h>
#include "sched.h"
#include "cpu.h"
#include "queue.h"

/*
 * Creates and initializes scheduler.
 *
 * @param ready_queue : Queue full of PCB structs.
 * @return : Scheduler ADT used with the other functions.
 */
Scheduler sched_create(Queue ready_queue)
{
        Scheduler sched;
        sched = (Scheduler)malloc(sizeof(struct Sched_Struct));

        sched->ready = ready_queue;
        sched->blocked = queue_CreateQueue(MAX_ELEMENTS);
        sched->running = queue_FrontAndDequeue(ready_queue);
        sched->locked = queue_CreateQueue(MAX_ELEMENTS);

        return sched;
}

/*
 * Does the context switch depending on what type of interrupt
 * gets passed into the function.
 *
 * Currently the function is not modular for simplicity sake, will
 * make more flexible once things come together.
 *
 * @param sched : Scheduler ADT
 * @param type : The type of interrupt that is triggering the context switch.
 */
void sched_switch(Scheduler sched, int type)
{
        PCBRecord temp;

        switch (type)
        {
        case INT_TIMER:
                sched->running->state = STATE_READY;

                queue_Enqueue(sched->running, sched->ready);
                sched->running = queue_FrontAndDequeue(sched->ready);

                sched->running->state = STATE_RUNNING;

                break;
        case INT_IO:
                temp = queue_FrontAndDequeue(sched->blocked);
                temp->state = STATE_READY;

                queue_Enqueue(temp, sched->ready);

                break;
        case INT_IO_REQUEST:
                sched->running->state = STATE_BLOCKED;

                queue_Enqueue(sched->running, sched->blocked);
                sched->running = queue_FrontAndDequeue(sched->ready);

                sched->running->state = STATE_RUNNING;

                break;
        }
}

/*
 * Returns the current PCB struct set as running.
 *
 * @param sched : Scheduler ADT
 * @return PCB : Currently running PCB
 */
PCBRecord sched_getRunning(Scheduler sched)
{
        return sched->running;
}

/**
 * Asks for lock of the semaphore, throws into locked queue
 * if access denied.
 *
 * @param sched : Scheduler ADT
 */
void sched_getLock(Scheduler sched)
{
        if (sched->semaphore != SEM_UNLOCKED &&
                sched->semaphore != sched->running->pid)
        {
                sched->running->state = STATE_BLOCKED;
                sched->running->waiting_on = BLOCKED_SEM;

                queue_Enqueue(sched->running, sched->locked);

                sched->running = queue_FrontAndDequeue(sched->ready);

                sched->running->state = STATE_RUNNING;
                sched->running->waiting_on = BLOCKED_NONE;

                return;
        }

        sched->semaphore = sched->running->pid;
        sched->running->owns = sched->running->pid;
}

/**
 * Release lock on semaphore and switch running process to
 * the next in locked queue.
 *
 * @param sched : Scheduler ADT
 */
void sched_releaseLock(Scheduler sched)
{
        if (sched->semaphore == sched->running->pid)
        {
                //Release ownership.
                sched->running->owns = SEM_UNLOCKED;
                sched->semaphore = SEM_UNLOCKED;

                if (queue_IsEmpty(sched->locked) == TRUE)
                        return;

                //First in locked queue gets semaphore, puts process into ready queue.
                PCBRecord temp = queue_FrontAndDequeue(sched->locked);
                sched->semaphore = temp->pid;
                temp->owns = temp->pid;
                temp->state = STATE_READY;
                temp->waiting_on = BLOCKED_NONE;

                queue_Enqueue(temp, sched->ready);
        }
}
